package org.andes.kownledge;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import lisp.SExp;

import org.andes.kownledge.operator.Effect;
import org.andes.kownledge.operator.Executable;
import org.andes.kownledge.operator.Foreach;
import org.andes.kownledge.operator.Macro;
import org.andes.kownledge.operator.MapMacro;
import org.andes.kownledge.operator.Operator;
import org.andes.kownledge.operator.Precondition;
import org.andes.kownledge.operator.Statement;
import org.armedbear.lisp.Cons;
import org.armedbear.lisp.LispObject;
import org.armedbear.lisp.Load;
import org.armedbear.lisp.Pathname;
import org.armedbear.lisp.Symbol;

/**
 * Created by IntelliJ IDEA.
 * User: wenfeix
 * Date: Feb 11, 2011
 * Time: 10:10:05 AM
 * To change this template use File | Settings | File Templates.
 */
public class Operators {
	public static Operators instance;
	public static Map<String,String> funMap=new HashMap<String,String>();
	public static Set<String> funSet=new CopyOnWriteArraySet<String>();
    
// Operators.cl
// Collin Lynch
// 10/26/2000
//; Modifications by Anders Weinstein 2002-2008
//; Modifications by Brett van de Sande, 2005-2008
//; Copyright 2009 by Kurt Vanlehn and Brett van de Sande
//;  This file is part of the Andes Intelligent Tutor Stystem.
//;
//;  The Andes Intelligent Tutor System is free software: you can redistribute
//;  it and/or modify it under the terms of the GNU Lesser General Public 
//;  License as published by the Free Software Foundation, either version 3 
//;  of the License, or (at your option) any later version.
//;
//;  The Andes Solver is distributed in the hope that it will be useful,
//;  but WITHOUT ANY WARRANTY; without even the implied warranty of
//;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//;  GNU Lesser General Public License for more details.
//;
//;  You should have received a copy of the GNU Lesser General Public License
//;  along with the Andes Intelligent Tutor System.  If not, see 
//;  <http://;www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////////////////
//
// This file defines a struct called operators for use with the Andes2 system.
// These are standard strips-style operators with the addition of three-tier
// hinting for use with the Andes system.  
//
// These operators can be loaded for use in any strips-style planning system
// and are inteded to support other similar projects to Andes2.
//
// The operators are indexed using a pair of hashtables and are meant to 
// be accessed using only those functions that have been labelled "public."
// In the future packaging will make this more explicit.
//
// The Operator's Fields are:
//  name: A unique name that will be used to index the operator for searching.
//
//  arguments:  A list of argument variables that will be used in defining 
//    the Operator's S-expression for later use.  This is stored in the 
//    stack for loop-prevention at runtime.
//
//  preconditions:  A list of preconditions that must be true before this 
//    operator can fire.  These will be tested by the system at runtime
//    as goals before the operator itself is fired.  
//
//  effects:  The effects that this operator has when fired.  At problem
//    solution time the system will test these goals to determine which
//    operator should be fired.  If the operator succeeds in being fired 
//    then these effects (with variables bound) will be added to the working
//    memory.  
//  
//  hint: This is a list of hints that will be used at runtime to guide the 
//    student along.  The list is of the form ((<type> <Hints>)...)  where
//    <Type> is one of [Point, Teach, Apply, Bottom-Out] and describes the
//      specificity of the hints within it.
//    <Hints> are list of operator hints of the form (<OpHintType> <Args>)
//      And will be evaluated as necessary to provide feedback.
//
//  HintVars:  A listing of all the variables used in the hints.  These will
//    Be used later for forming the hints at runtime.  
//  
//  specifications:  A Comment-ish stringdescribing the operator itself and
//    later useful for documentation.
//
//  Variables:  A list of all the variables that appear in this operator 
//    taken from the hints, args, preconditions, effects, etc.  This list
//    will be stored later with the bindings for runtime use.
//  
//  Features:  A list of operator features e.g. PSM, and 'Ordered' which are
//    used for specialized filtering. 
//              
//  CogLoad:  At present this is unused.  It defines the "Cognitive Load"
//    (Difficulty) of the operator and, in-turn the likelyhood that the 
//    students would be to use it.  
//
// At problem solution time, the system will backward-chain using the operators.  
// Given the current sought it will select the subset of operators containing 
// effects that match it.  For each operator it will split the search and attempt
// to solve the preconditions.  If an operator succeeds it will move on etc.
// The Operator structs will become the basis of the solution graphs and will be 
// stored for later use along with their variable values.  These values will then
// be used to provide hints at runtime.


//=======================================================================
// Parameters
////////////////////////////////////////////////////////////////////////;
// *Operators-By-Effect*
// This parameter contains a global hashtable that we will use to store 
// and access the operators at runtime.
// When operators are defined they are registered with the *Operators*
// hastable for future use.
//
// Since we will not be adding operators at runtime we can trade addition
// cost for access speed.

/*(defparameter *Operators-by-Effect* (make-hash-table :test #'equalp
						     :size 20
						     :rehash-size 5
						     :rehash-threshold .8)
  "The hashtable where we will index operators by their effects for searching.") */

////////////////////////////////////////////////////////////////////////////////
// *Operators-by-name*
// A hashtable in which we will store operators by name for efficient selection
// when necessary.

/*(defparameter *Operators-By-Name* (make-hash-table :test #'equalp
						   :size 20
						   :rehash-size 5
						   :rehash-threshold .8)
  "A hashtable in which we will store the operators by their names for efficient access.") */


//========================================================================
// Operator Struct
//
// The operator struct defines a strips based operator which is defined
// by a unique name, a set of arguments, a list of preconditions and a
// list of effects.
//
// In the Andes solution system these operators are used in a goal-first
// manner.  However they can be employed in any reasonable strips system.
// 
// Future versions of this structure will contain help strings for the 
// Andes/Atlas help systems.
//
// Note:  The definition of this structure includes the creation of 
// operator-p to test for structures.  hence the need for a different
// name for the s-expression form.

/*(defstruct (operator (:print-function print-operator))
  name             //Unique Operator name.
  arguments        //List of operator arguments for use in defining the operator s-expression.
  preconditions    //The preconditions for this operator to occur.

  effects          //The operator's effects.
  hint             //The hint field.
  HintVars         // Argument elements used to compare two opapps at help time for use by 
                   // the comparison system.  
  
  specifications   //The operaor specifications.

  Variables        //A list consisting of all the variables in this operator.
  
  Features         //A list of operator features e.g. PSM, and Ordered which
                   //are used to determine aspects of the operator.
  
  (CogLoad 1 :type real)      //The 'cognitive load' of this operator used for min searching.
                              // Needs to be edded into the loop.

  order           ;List of dotted pairs giving order specifications
		  ;when several operators apply choose those with maximal order 
  )*/

	
	;
	//int operindex=3;
	public void readOperators()throws Exception{
		Pathname pathname = new Pathname("KB/problem-solving.cl");
		readFile(pathname);
		pathname = new Pathname("KB/kinematics.cl");
		
		readFile(pathname);
		
		for(String funName:funSet)
			System.out.println("funName:"+funName);
		
	}
	private void readFile(Pathname pathname)throws Exception{
		LispObject lo=Load.loadOnly(pathname, false, false, true);
		LispObject[] operators=lo.copyToArray();
		for(LispObject operator:operators){
			LispObject[] key_values=operator.copyToArray();
			String type=key_values[0].getStringValue();
			if(type.equalsIgnoreCase("defoperator"))
				readOperator(key_values);
		}
		
	}
	public void readOperator(LispObject[] key_values) throws Exception{
		int operindex=1;
		Operator op=new Operator();
		op.setName(key_values[1].getStringValue());
		while(operindex+2<key_values.length){
			processOperatorDefElem(op,key_values,operindex+=2);
		}
		register_operator(op);
		
	}
	private void processOperatorDefElem(Operator op,LispObject[] key_values,int operindex){
		try {
			String key=key_values[operindex].getStringValue();
			//System.out.println(op.name()+":key:"+key);
			LispObject value=key_values[operindex+1];	
			if(key.equals("PRECONDITIONS")){	
				processPreconditions(value,op);
			}else if(key.equals("EFFECTS")){	
				processEffects(value,op);
			}
		} catch (Error e) {
			e.printStackTrace();
		}		
	}
	private void processPreconditions(LispObject value,Operator op){
		List<LispObject> preconditions=value.toList();
		for(LispObject ls:preconditions){
			op.preconditions().add(processPrecondition(ls));
		}
		
		
	}
	private Precondition processPrecondition(LispObject lo){
		if(executable_p(lo)){
			return buildExecutable(lo);
		}else if(precond_macro_p(lo)){
			return buildMacro(lo);
		}else
			return buildStatement(lo);
		
	}
	public  boolean executable_p(Object op){
		if(op instanceof Cons){
			LispObject name=((Cons) op).car();
			if(name instanceof Symbol){
				String type=((Symbol)name).getName();
				return type.equalsIgnoreCase("bind")||
				 type.equalsIgnoreCase("not")||
				 type.equalsIgnoreCase("in-wm")||
				 type.equalsIgnoreCase("wm-or-derive")||
				 type.equalsIgnoreCase("add-to-wm")||
				 type.equalsIgnoreCase("test")||
				 type.equalsIgnoreCase("any-member")||
				 type.equalsIgnoreCase("debug")||
				 type.equalsIgnoreCase("rdebug")||
				 type.equalsIgnoreCase("count")||
				 type.equalsIgnoreCase("setof");
				
			}
		}
		return false;
	}
//	(defun precond-macro-p (M)
//	  "Return t iff Exp is a Macro expression."
//	  (and (listp M)
//	       (member (car M) '(foreach map))))
	public  boolean  precond_macro_p(LispObject lo){
		if(lo instanceof Cons){
			String name=((Symbol)lo.car()).getName();
			return name.equalsIgnoreCase("foreach")|| name.equalsIgnoreCase("map");
			
		}
		return false;
	}
	    		  
//	 type.equalsIgnoreCase("not")||
//	 type.equalsIgnoreCase("in-wm")||
//	 type.equalsIgnoreCase("wm-or-derive")||
//	 type.equalsIgnoreCase("add-to-wm")||
//	 type.equalsIgnoreCase("test")||
//	 type.equalsIgnoreCase("any-member")||
//	 type.equalsIgnoreCase("debug")||
//	 type.equalsIgnoreCase("rdebug")||
//	 type.equalsIgnoreCase("count")||
//	 type.equalsIgnoreCase("setof");
	private Executable buildExecutable(LispObject lo){
		LispObject name=((Cons) lo).car();		
		String type=((Symbol)name).getName();
		if(type.equalsIgnoreCase("bind")){
			return Executable.buildBind(lo);
		}else if(type.equalsIgnoreCase("not")){
			return Executable.buildNot(lo);
		}else if(type.equalsIgnoreCase("in-wm")){
			return Executable.buildInwm(lo);
		}else if(type.equalsIgnoreCase("wm-or-derive")){
			return Executable.buildWmOrDerive(lo);
		}else if(type.equalsIgnoreCase("add-to-wm")){
			return Executable.buildAddToWm(lo);
		}else if(type.equalsIgnoreCase("test")){
			return Executable.buildTest(lo);
		}else if(type.equalsIgnoreCase("any-member")){
			return Executable.buildAnyMember(lo);
		}else if(type.equalsIgnoreCase("count")){
			return Executable.buildCount(lo);
		}else if(type.equalsIgnoreCase("setof")){
			return Executable.buildSetof(lo);
		}
		return null;
	}
	private Macro buildMacro(LispObject lo){
		LispObject name=((Cons) lo).car();		
		String type=((Symbol)name).getName();
		if(type.equalsIgnoreCase("map")){
			MapMacro map=new MapMacro();
			return map;
		}else if(type.equalsIgnoreCase("foreach")){
			Foreach foreach=new Foreach();
			return foreach;
		}
		
		System.out.println("There is error in buildMacro");
		return null;
	}
	
	private Statement buildStatement(LispObject lo){
		
			if(lo instanceof Cons){
				LispObject name=((Cons) lo).car();		
				String type=((Symbol)name).getName();
				Statement statement=new Statement();
				statement.setPredicate(type);
				return statement;
			}else{
				return null;//xwf
			}
				
			
		
	}
	private void processEffects(LispObject value,Operator op){
		List<LispObject> effects=value.toList();
		for(LispObject ls:effects){
			op.effects().add(processEffect(ls));
		}		
	}
	private Effect processEffect(LispObject lo){
		LispObject name=((Cons) lo).car();		
		String type=((Symbol)name).getName();
		Effect effect=new Effect();
		effect.setPredicate(type);
		return effect;
	}
	
	
	
	
	public static List<Operator> ops=new ArrayList<Operator>(); 
	
	private static Map<String,Operator> Operators_By_Name=new HashMap<String,Operator>();

//----------------------------------------------------------------------
// print-operator
// Pretty-print the specified operator to the specified stream.
//
// At the moment this is incomplete.
//

/*(defun print-operator (Operator Stream Level)
  "Print out the specified operator."
  (pprint-Indent :block Level)
  (format Stream
	  "(~A ~A)"
	  (operator-name Operator)
	  (operator-arguments Operator)))*/


//-----------------------------------------------------------------------------
// Operator Definition.
////////////////////////////////////////////////////;
// defoperator (public) 
// define an operator with the given values and add
// it to the *Known-Operators* list.
//
// Arguments:
//  Name:  The operator name.
// 
// &key
//  Arguments:  The operator Arguments.
//  Precondintions:  The operator preconditions.
//  Effects:  The operator's effects.
//
// Returns: A new operator with the given values.
//

/*(defmacro defoperator (Name Arguments
		       &key Preconditions Effects 
			    Hint Specifications Features
			    Load (Order '((default . NORMAL))) )
  
  "Define a new operator with the specified values and add it to *operators*."
  
  (let ((Op (eval `(make-operator	;Produce the operator struct.
		    :Name ',Name 
		    :Arguments ',Arguments 
		    :Preconditions ',Preconditions 
		    :Effects ',Effects
		    :Hint ',(subst-nlgs-hints Hint) ;Substitute the NLG functions into the system.
		    :Specifications ',Specifications
		    :Order ',(sublis *op-order-ids* ; replace order symbols with numerical values
		                ; ensure order list contains a value for default group
		                (adjoin '(default . NORMAL) Order
				        :test #'(lambda(x y) (eq (first x) (first y)))))
		    :CogLoad ',(if Load 
				   Load
				 1)))))
    
    (if (not (list-of-atoms-p Features)) ;Ensure that the features list is valid.
	(error "The specified Features list for ~A is invalid ~A." ;if not signal an error. 
	       Name Features) 
      (setf (Operator-Features Op) Features)) ;otherwize set the features.
	
                                                                 
    (setf (Operator-Variables Op)	;Set the variables list.
      (get-operator-variables Op))

    (setf (Operator-hintVars Op)	;Set the hintvars list.
      (get-operator-hintvars Op))

    (Register-operator Op)		;Store the operator struct.
    
    Op))				;and return it. */

    public void defoperator(){

    }

// mapping for symbols which may be used as operator order values 
/*(defvar *op-order-ids* '(
    (LOWEST . 1)
    (LOW . 3)
    (NORMAL . 5)
    (HIGH . 7)
    (HIGHEST . 9) 
  ))*/

//////////////////////////////////////////////////////////////////////////;
// subst-nlg-funcs
// The assumption made by the hint system is the operator hint variables
// need to be nlg'd at helptime.  The nlg function itself is located 
// elsewhere.  This function takes a hint spec of the form:
//            (<type> <specs>)
//            where specs are of the form.
//            (<Type> <Format Str> <Vars>)
// where each var is either an atom such as ?body or a list such as 
// (?body 'defnp) and substitutes a 'nlg onto the fromt of all lists
// or replaces atoms with lists (nlg ?body).
/*(defun subst-nlgs-hints (hints)
  "Subst the nlgs into the hints."
  (loop for h in hints
      collect (cons (OpHint-type h) 
		    (subst-nlgs-specs (Ophint-Hintspecs H)))))
  
(defun subst-nlgs-specs (Specs)
  (loop for S in Specs
      collect (append (var-free-hintspec S)
		      (subst-nlgs-specvars (HintSpec-Vars S)))))

(defun subst-nlgs-specvars (Vars)
  (mapcar #'(lambda (v) 
	      (if (listp v)
		  (cons 'nlg v)
		(list 'nlg v)))
	  Vars))*/





////////////////////////////////////////////////////////////////////////////////////
// Resgister-operator
// Index the specified operator in the *Operators-by-Effect* and *Operators-by-name*
// 
// Arguments: Op: The operator being indexed.
//
// Returns: The operator after indexing (unchanged)

/*(defun register-operator (Op)
  "Index the specified operator in *Operators-by-Effect* and *Operators-by-name*"
  (dolist (Eff (Operator-Effects Op))                                             //for each effect Eff of the operator.   
    (if (not (member Op (gethash (car Eff) *Operators-by-Effect*))) //If Op is not already associated with
       	                                                            //the car of Eff in *Operators-By-Effect*
	(push Op (gethash (car Eff) *Operators-By-Effect*))))                     //then associate it.
  (setf (gethash (Operator-name Op) *Operators-By-Name*) Op)                      //Index the operator by name.
  Op)                                                                              //return the operator.

  */
    public void register_operator(Operator op){
    	for(Effect effect:op.effects()){
    		String name=effect.getPredicate();
    		List<Operator> list=Operators_By_Effect.get(name);
    		if(list==null){
    			list=new ArrayList<Operator>();
    			list.add(op);
    			Operators_By_Effect.put(name, list);
    		}else{
    			list.add(op);
    		}    			   		
    	}   	
    	Operators_By_Name.put(op.name(), op);
    }



//---------------------------------------------------------------------------------
// Operator selection
// These functions are called at runtime by the problem solver and the 
// help system to retreive the operators for solution or help queries.
////////////////////////////////////////////////////////////////////;
// get-operator-by-name (public)
// Get the specified operator by its name.
//
// Arguments: Opname: The operator name.
// Returns: The Operator iff it is indexed by name in *Operators-by-name* 
//          or nil if it is not.

/*(defun get-operator-by-name (opname)
  "Get the specified operator by name or nil if it does not exist."
  (gethash Opname *Operators-By-Name*))*/
    public void getOperatorByName(){

    }


    private static HashMap<String,List<Operator>> Operators_By_Effect=new HashMap<String,List<Operator>>();
//////////////////////////////////////////////////////////////////////////////////;
// get-operators-by-effect (public)
// get the specified operators that have effects of the specified predicate type.
//
// Arguments: Predicate: The predicate e.g. 'Variable' that we are seaching for.
// Returns: A list of operators who have at least one effect of the specified type
//          ornil if noe exist.

/*(defun get-operators-by-effect (Predicate)
  "Obtain a list of operators that have an effect of the specified predicate type or nil if none exist."
  (gethash Predicate *Operators-By-Effect*))*/
    public static List<Operator> getOperatorByEffects(String Predicate){
    	return Operators_By_Effect.get(Predicate.toUpperCase());

    }

// Following utility mainly for kb maintenance. takes either atom or form 
/*(defun list-ops (Predicate-or-Form)
  "return list of operators with effects using specified predicate or unifying with form"
  ; turn atomic argument into unify pattern
  (let ((pat (if (atom Predicate-or-Form) `(,Predicate-or-Form . ?rest)
                  Predicate-or-Form)))
   (mapcar #'operator-name
	   (remove-if-not #'(lambda (op)
	                 (find pat (operator-effects op) :test #'unify))
	       (get-operators-by-effect (first pat))))))*/
    public void listOps(){

    }


//////////////////////////////////////////////////////////////////////;
// get-operator-by-tag
// given an operator tag (<Name> <args>) get the
// operator that matches it.
/*(defun get-operator-by-tag (tag)
  (get-operator-by-name (car tag)))*/
    public static Operator getOperatorByTag(SExp op){
    	return Operators_By_Name.get(op);

    }

//////////////////////////////////////////////;
// Clear-Ops (public)
// Clear the operator indexes.

/*(defun clear-ops ()
  "Clear the *Operators-By-Effect* and *Operator-By-Name* indexes."
  (clrhash *Operators-By-Name*)
  (clrhash *Operators-By-Effect*)) */
    public void clearOps(){

    }





//-----------------------------------------------------------------------------
// Operator-var-copy (public)
// In order to avoid unconcious overwriting and to permit recursion this 
// function systematically replaces all variables in an operator with a 
// matching renamed set.

/*(defun operator-var-copy (Operator)
  "Obtain a copy of Op with the vars changed."

  (let* ((Op (copy-Operator Operator))
	 (oplist (list (Operator-arguments Op)         //Generate a single expression of the operator.
		       (Operator-Preconditions Op)
		       (Operator-Effects Op)
		       (Operator-Variables Op)
		       (Operator-hintvars Op))))
    
    (setq oplist (rename-variables oplist))            //replace all the variables with new vars.
    (setf (operator-arguments Op) (nth 0 Oplist))      //Set the new Operator-arguments.
    (setf (operator-preconditions Op) (nth 1 Oplist))  //Set the new preconditions.
    (setf (operator-effects Op) (nth 2 Oplist))        //set the new effects.
    (setf (Operator-Variables Op) (nth 3 Oplist))
    (setf (Operator-hintvars Op) (nth 4 Oplist))
    Op))                                               //return the new form of Op
   */
    public void operatorVarCopy(){

    }


// --------------------------------------------------------------------
// Operator hints
// The Operator hints consists of a list of point, teach and 
// aplly hints of the form (<Type> . <Specs>) where type is one
// of POINT, TEACH, or APPLY.  <Specs> is a list of hint 
// specifications of the form (<class> <string> . <vars>)
// where:
//   <Class> is one of STRING KCD MINILESSON
//   <String> is a format string complete with ~A's.
//   <Vars> is alist of operator vars that will be
//          subsituted into the string via a format.
//
// The operator hints are designed to be passed to the students
// when 

// The operator hints can be turned into tutor turns by passing
// them to the appropriate code in the HelpStructs/Tutorturn.cl
// file.  The code in this sections allows you to retreive the
// tutorturns with the variable values substituted into them.
////////////////////////////////////////////////////////////////;
// get-op-hints
// Given an operator struct and an optional list of operator values 
// the hints themselves from the operator with the values substituted
// in if supplied.
/*(defun get-op-hints (Op &optional (Values ()))
  "Get the operator hints with vals substituted."
  (when Op
    (subst-bindings
     (bind-operator-vars Op Values)
     (Operator-Hint Op))))*/
    public void getOpHints(){
        
    }


//--------------------------------------------------------------------
// Individual Operator Hints.
// Are of the form (<Type> <Hints>) Where
//  <Type> is one of [Point, Teach, Apply, Bottom-Out] and describes
//   The specificity of the hint.
//
//  <Hints> are a list of HintSpecs which will be selected from and 
//   presented to the student as necessary.
//
// This code provides access to the individual operator hints. 

/*(defparameter **OpHint-Types** '(Point Teach Apply Bottom-Out))

(defun OpHint-type (hint)
  "One of (Point, Teach apply, etc.)."
  (car hint))

(defun OpHint-Hintspecs (hint)
  "Get the individual hint-specs from the hint."
  (cdr Hint))

(defun OpHint-P (Hint)
  (and (listp Hint)
       (member (car Hint) **OpHint-Types**)
       (null (remove-if #'HintSpec-P (cdr Hint)))))

(defun filter-hints-by-type (hints types)
  (remove-if-not #'(lambda (x) (member x types :test #'eql))
		 hints :key #'OpHint-type))

//; Hinting the step itself involves collecting the hint specs
//; from the operator and returning the result.
(defun collect-step-hints (step &key (types nil))
  "Collect the hints from an op step."
  (let ((hints (get-op-hints 
		(get-operator-by-tag (csdo-op step))
		(csdo-varvals step))))
    (if Types (filter-hints-by-type hints types)
      hints)))


//--------------------------------------------------------------------
// HintSpecs
// The Hint Specifications are of the form: (<Type> <String> <Vars>)
//  <Type> is one of [String, kcd, Minilesson, Eval, Function]
//    These identify the appropriate response to the hint at runtime
//  <Form> is an atom (See below)
//  <vars> is an optional list of variables that, if present might
//    be substituted in for later use. 
//
// Hintspecs will be interpreted differently at runtime depending 
// upon their type.  The following describes the cases.
//
//  String: the Form is a format string which will be called along
//    with the values of the (optional) variables.
//
//  kcd: the Form is a string that will be (after substitution of
//    the vars) used to call the appropriate kcd by name.
//
//  Minilesson:  the form is a string that (after subst) will be 
//    treated as a minilesson filename and loaded by the help sys.
//
//  Eval:  The form is a lisp-expression that will be evaluated to
//    Produce a list of hints that will be used (along with the
//    remaining hints to generate a Hint Sequence.
//
//  Function:  The form is a function or function name that will be
//    fuincalled to produce a hint-sequecne.  Any hints after this
//    in the list will be ignored.  

//sbcl has problems with defconstant, see "sbcl idiosyncracies"
(#-sbcl defconstant #+sbcl sb-int:defconstant-eqx 
	**HintSpec-Types** '(String KCD MiniLesson Eval Function)
	#+sbcl #'equalp)

(defun HintSpec-Type (Spec)
  "Get the hintspec's class" 
  (nth 0 Spec))

(defun HintSpec-form (Spec)
  "Get the hintspec's form."
  (nth 1 Spec))

(defun hintspec-vars (Spec)
  "Get the hintspec's variables."
  (subseq Spec 2))

(defun var-free-hintspec (spec)
  "Get the hintspec sans vars."
  (subseq spec 0 2))

(defun Hintspec-P (Spec)
  "Is the supplied elt a HintSpec."
  (and (listp Spec) (member (car Spec) **HintSpec-Types**)))

(defun format-hintspec (Spec)
  "Get a string form of the hintspec."
  (if (stringp Spec) Spec
    (apply #'format nil 
	   (hintspec-form Spec)
	   (mapcar #'eval-spec-arg (hintspec-vars Spec)))))


(defun eval-spec-arg (arg)
  (if (and (consp arg) 
           (eq (first arg) 'nlg))
    (apply #'nlg (rest arg))	; if nlg'ing, don't evaluate rest
    (andes-eval arg)))


//;----------------------------------------------------------------------------
//; Collecting hint specs
//; Given an operator it may be necessary to extract specific elements such
//; as the kcd or minilesson names that might be hinted within it.  This code
//; does that returning the kcd name or minilesson names (all we care about 
//; for hints of those types) to the user.

(defun collect-operator-kcds (op)
  "Collect the kcd's hinted in op."
  (remove-if 
   #'null
   (mapcan #'(lambda (h) 
	       (mapcar #'hintspec-form
		       (collect-ophint-types 'KCD h)))
	   (operator-hint op))))
   
(defun collect-operator-Minilessons (op)
  "Collect the kcd's hinted in op."
  (remove-if 
   #'null
   (mapcan #'(lambda (h) 
	       (mapcar #'hintspec-form 
		(collect-ophint-types 'Minilesson h)))
	   (operator-hint op))))


(defun collect-ophint-types (type hint)
  "Collect the kcds from an ophint."
  (remove-if
   #'null (mapcar #'(lambda (s)
		      (if (eq (hintspec-type s) type) s))
		  (Ophint-hintspecs hint))))



   


//;===========================================================
//; Mapping from operator tags to non-op-elements.

//; Given a list of operator tags collect the matching
//; operators.
(defun map-optags->operators (tags)
  "Collect the operators for the tags."
  (remove-duplicates
   (remove-if 
    #'null (mapcar #'get-operator-by-tag tags))))

//; Given a list of operators map them to the list of 
//; kcds that appear within their hint specifications.
(defun map-operators->kcds (operators)
  (remove-duplicates
   (mapcan #'collect-operator-kcds
	   operators)))

//; Given a list of operators map them to the list of 
//; minilessons that appear within their hint specifications.
(defun map-operators->minilessons (operators)
  (remove-duplicates
   (mapcan #'collect-operator-kcds
	   operators)))


(defun map-optags->kcds (tags)
  "Map the operator tags to the kcds."
  (map-operators->kcds 
   (map-optags->operators tags)))


(defun map-optags->minilessons (tags)
  "Map the operator tags to the kcds."
  (map-operators->minilessons
   (map-optags->operators tags)))

  



//===========================================================
// trace-operators 
// trace the relevant operator functions.

(defun trace-operators ()
  "Trace the operator functions."
  
  (trace defoperator)
  (trace get-operator-by-name)
  (trace get-operators-by-effect)
  (trace operator-var-copy)
  )		 


(defun trace-depops ()
  (trace operator-struct)
  (trace is-operator)
  (trace operator-s-expression))*/

    
}
